﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Linq.Expressions;
using System.Reflection;
/******
Assign       ,generate entity, total 10000 rows, time: 3
Emit         ,generate entity, total 10000 rows, time: 7
Expression   ,generate entity, total 10000 rows, time: 38
Reflection   ,generate entity, total 10000 rows, time: 39
Delegate     ,generate entity, total 10000 rows, time: 137

 * ****/
namespace TEmit
{
    /// <summary>
    /// 
    /// </summary>
    public static class ToEntityByExpression
    {
        public static List<T> GetEntity<T>(DataTable dt)
        {
            List<T> lst = new List<T>();
            Dictionary<string, Func<T, object, object>> dic = new Dictionary<string, Func<T, object, object>>();
            foreach (DataColumn dc in dt.Columns)
            {
                PropertyInfo pi = typeof(T).GetProperty(dc.ColumnName);
                if (!dic.ContainsKey(dc.ColumnName))
                {
                    Func<T, object, object> fc = SetDelegate<T>(pi.GetSetMethod(), pi.PropertyType);
                    dic.Add(dc.ColumnName, fc);
                }
            }

            foreach (DataRow dr in dt.Rows)
            {
                T model = (T)Activator.CreateInstance(typeof(T));

                foreach (DataColumn dc in dt.Columns)
                {
                    Func<T, object, object> fc = dic[dc.ColumnName];
                    fc(model, dr[dc.ColumnName]);
                }
                lst.Add(model);
            }

            return lst;
        }

        

        static Func<T, object,object> SetDelegate<T>(MethodInfo m, Type type)
        {
            ParameterExpression param_obj = Expression.Parameter(typeof(T), "obj");
            ParameterExpression param_val = Expression.Parameter(typeof(object), "val");
            UnaryExpression body_val = Expression.Convert(param_val, type);
            MethodCallExpression body = Expression.Call(param_obj, m, body_val);
            Action<T, object> set = Expression.Lambda<Action<T, object>>(body, param_obj, param_val).Compile();
            
            return (instance, v) =>
            {
                set(instance, v);
                return null;
            };
        }
    }
}
